package cn.gbase.rxdata;

import com.google.common.collect.Lists;
import net.sf.cglib.proxy.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.List;

public class ResultSetDynamicProxyHandler implements InvocationHandler, Observerable {
    private ResultSet realResultSet;
    private List<Observer> lists = Lists.newArrayList();


    private ResultSetDynamicProxyHandler(ResultSet realResultSet){
        this.realResultSet = realResultSet;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("next")){
            this.change();
        }
        Object o = method.invoke(realResultSet,args);
        return o;
    }

    @Override
    public void regObserver(Observer observer) {
        lists.add(observer);
    }

    @Override
    public void delObserver(Observer observer){
        lists.remove(observer);
    }

    @Override
    public void change() {
        lists.stream().forEach(it -> it.update());
    }

    @Override
    public void change(String msg) {
        lists.stream().forEach(it -> it.update(msg));
    }

    public static ResultSet newInstanceWithObserver(ResultSet realResultSet,Observer observer){

        ResultSetDynamicProxyHandler rdph = new ResultSetDynamicProxyHandler(realResultSet);
        rdph.regObserver(observer);
        return (ResultSet) Proxy.newProxyInstance(rdph.getClass().getClassLoader(),new Class[]{ResultSet.class},rdph);
    }

    public static ResultSet newProxyInstance(ResultSet realResultSet) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ResultSetDynamicProxyHandler rdph = new ResultSetDynamicProxyHandler(realResultSet);
        Enhancer e = new Enhancer();
        e.setSuperclass(Proxy.class);
        e.setInterfaces(new Class[]{ResultSet.class});
        e.setCallbackTypes(new Class[]{
                InvocationHandler.class,
                NoOp.class,
        });
        e.setCallbackFilter(JUST_OBJECT_METHOD_FILTER);
        e.setUseFactory(false);
        return (ResultSet) e.createClass().getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ rdph });
    }

    private static final CallbackFilter JUST_OBJECT_METHOD_FILTER = new CallbackFilter() {
        public int accept(Method method) {
            if (method.getDeclaringClass().getName().equals("java.lang.Object")) {
                String name = method.getName();
                if (name.equals("next")) {
                    return 1;
                }
            }
            return 0;
        }
    };


}
